﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;

using AForge.Neuro;
using AForge.Neuro.Learning;
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.VFW;


namespace AIthin.Controls
{
    public partial class NeuroControl : UserControl
    {
        /// <summary>
        /// Unique network name for this network
        /// </summary>
        public String NetworkName = "NoName";
        /// <summary>
        /// oeffnet neues Instanz vom AVI Datei
        /// </summary>
        AVIReader reader = new AVIReader();
        /// <summary>
        /// Bitmap die abbildet Reality in den Zeitschritt
        /// </summary>
        Bitmap bmRealitySnap;
        /// <summary>
        /// Bitmap die Abbildet InputSignale
        /// </summary>
        Bitmap bmInput;
        /// <summary>
        /// Bitmap die Abbildet Output Signale
        /// </summary>
        Bitmap bmOutput;

        RDBExpressDataSet DB;

        String LearnStream = "..\\Streams\\default.avi";
        String TestStream = "..\\Streams\\default.avi";
        int framesCount = 0;
        /// <summary>
        /// Anzal Output Signals wird von der erste Zeile Output genommen
        /// wieviel punkten sind Weiss so viel Output Signals sind da.
        /// </summary>
        int OutputsCount = 0;
        double error = 1.0;
        int Deep;
        double[][] input = null; // 1. - Lernset Nummer,  2. -Inputsignal Belegung
        double[][] output = null; // 1. - Lernset Nummer,  2. -Outputsignal Belegung
        double[] outputReal = null; // 1. - Lernset Nummer,  2. -Ausgabe Outputsignal Belegung
        ActivationNetwork network;
        ISupervisedLearning teacher;
        int Step;
        /// <summary>
        /// Constructor
        /// </summary>
        public NeuroControl()
        {
            InitializeComponent();
            this.SetStyle(0
                              | ControlStyles.OptimizedDoubleBuffer //DoubleBuffer
                              | ControlStyles.UserPaint
                              | ControlStyles.AllPaintingInWmPaint
                              , true);

            this.UpdateStyles();

            labelLearnFile.Text = "";

            bmRealitySnap = new Bitmap(128, 64, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmInput = new Bitmap(16, 16, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmOutput = new Bitmap(16, 16, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            buttonStopLearn.Enabled = false;

            DB = new RDBExpressDataSet();

            // dummy network
            network = new ActivationNetwork((IActivationFunction)new BipolarSigmoidFunction(1.6), 4, 2);
        }
        int ProcessLearn()
        {
            DataTable tablePoints = pointsTableAdapter1.GetData();
            DataTable tableCurves = curvesTableAdapter1.GetData();

            // Set Learn Patterns
            while ((Step = reader.Position) < reader.Length)
            {
                bmRealitySnap = reader.GetNextFrame();
                SetInput(Step);
                // Abbilden Input Signal
                DrawInput(Step);
                SetOutput(Step);

                labelMinOutput.Text = output[Step][0].ToString();
                labelMaxOutput.Text = output[Step][1].ToString();
                labelLearnCycles.Text = Step.ToString();
                Invalidate();
            }
            // teach
            error = 1.0;
          
            if (tablePoints.Rows.Count == 0)
                MessageBox.Show("Update points fPointsRow is 0, please add one row");
            int FirstPoint = Convert.ToInt32(tablePoints.Rows[tablePoints.Rows.Count - 1]["ID"]) + 1;
            int LastPoint = FirstPoint;

            int k = 0;
            double errorOld = 0;
            while (error > 1 & k < 2000000)
            {
                // run learning iteration
                error = teacher.RunEpoch(input, output);
                labelError.Text = error.ToString();
                labelLearnCycles.Text = k++.ToString();
                if (Math.Abs(error - errorOld) > 0.01)
                   pointsTableAdapter1.Insert(LastPoint++, Convert.ToInt16(k), Convert.ToSingle(error));
                errorOld = error;
                // compute Output
      //          outputReal = network.Compute(input[0]);
                // show output
                DrawOutputReal();
    //            outputReal = network.Compute(input[1]);
                labelMinOutput.Text = outputReal[0].ToString();
                labelMaxOutput.Text = outputReal[1].ToString();
                // show output
   //             DrawOutputReal();
                Invalidate();
                Application.DoEvents();
            }
            try
            {
                pointsTableAdapter1.Update(DB.Points);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Update points failed");
            }


            // tablePoints = pointsTableAdapter1.GetData();
            //int LastPoint = Convert.ToInt32(tablePoints.Rows[tablePoints.Rows.Count - 1]["ID"]);
            //DB.Curves.AddCurvesRow(FirstPoint, Convert.ToInt32(DB.Points.Rows[DB.Points.Rows.Count - 1]["ID"]));
            curvesTableAdapter1.Insert(tableCurves.Rows.Count + 1, FirstPoint, LastPoint - 1);

            try
            {
                curvesTableAdapter1.Update(DB.Curves);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Update curves failed");
            }

            return k;
        }
        private void InitLearning(int[] neuronsCount, double SV, int PopulationSize)
        {
            Deep = reader.Height;
            int LearnSets = reader.Length;

            GetOutputsCount();

            input = new double[LearnSets][];
            output = new double[LearnSets][];
            outputReal = new double[OutputsCount];

            // anpassen Anzahl Output Neurons
            neuronsCount[neuronsCount.Length - 1] = OutputsCount;

            bmRealitySnap = new Bitmap(2 * Deep, Deep, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmInput = new Bitmap(Deep, Deep, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmOutput = new Bitmap(Deep, Deep, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            for (int i = 0; i < LearnSets; i++)
            {
                input[i] = new double[Deep * Deep];
                output[i] = new double[OutputsCount];
            }
        }
        private void GetOutputsCount()
        {
            bmRealitySnap = reader.GetNextFrame();

            OutputsCount = 0;
            for (int i = 0; i < Deep; i++)
                if (ColorToShort(bmRealitySnap.GetPixel(Deep + i, 0)) > 0) OutputsCount++;

            reader.Position = 0;
        }
        private short ColorToShort(Color inColor)
        {
            return Convert.ToInt16((inColor.R + inColor.G + inColor.B) / 3.0);
        }
        private void DrawOutputReal()
        {
            int[] OutputColor = new int[OutputsCount];
            for (int i = 0; i < OutputsCount; i++)
            {
                OutputColor[i] = System.Convert.ToInt16(Math.Abs(outputReal[i] * 255));
                for (int k = 0; k < Deep; k++)
                    bmOutput.SetPixel(k, i + 1, Color.FromArgb(OutputColor[i], OutputColor[i], OutputColor[i]));
            }
        }
        private void DrawInput(int LearnSetNummer)
        {
            int m = 0;
            for (int j = 0; j < Deep; j++)
                for (int ki = 0; ki < Deep; ki++)
                {
                    if (input[LearnSetNummer][m++] > 0)
                        bmInput.SetPixel(j, ki, Color.White);
                    else
                        bmInput.SetPixel(j, ki, Color.Black);
                }
        }
        private void SetInput(int LearnSetNummer)
        {
            int Index = 0;
            for (int i = 0; i < Deep; i++)
                for (int j = 0; j < Deep; j++)
                    input[LearnSetNummer][Index++] = (ColorToShort(bmRealitySnap.GetPixel(i, j)) > 0) ? 1 : 0;
        }
        private void SetOutput(int LearnSetNummer)
        {
            for (int i = 0; i < OutputsCount; i++)
                output[LearnSetNummer][i] = (ColorToShort(bmRealitySnap.GetPixel(Deep, i + 1)) > 0) ? 1 : 0;
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.DrawImage(bmRealitySnap, 10, 10);
 //           g.DrawImage(bmInput, 10, 80);
  //          g.DrawImage(bmOutput, 74, 80);
        }
        private void buttonStopLearn_Click(object sender, EventArgs e)
        {
            buttonStopLearn.Enabled = false;
        }
        private void buttonStep_Click(object sender, EventArgs e)
        {
            Step = reader.Position;
            labelLearnCycles.Text = Step.ToString();

            if (Step < framesCount)
            {
                bmRealitySnap = reader.GetNextFrame();
                SetInput(1);
       //         SetOutput(1);
                // Abbilden Input Signal
      //          DrawInput(1);
                // compute Output
                //TODO wenn war noch nicht gelernt Test funktioniert nicht
                outputReal = network.Compute(input[1]);
                // show output
    //            DrawOutputReal();
                labelMinOutput.Text = outputReal[0].ToString();
                labelMaxOutput.Text = outputReal[1].ToString();
                Refresh();
            }
            else
                reader.Close();
            Invalidate();
        }
        private void buttonResearch_Click(object sender, EventArgs e)
        {
            reader.Close();
            buttonStopLearn.Enabled = true;
            DataTable tableParameterSet = parameterSetTableAdapter1.GetData();
            DataTable tableExperiments;
            DateTime StartTime;
            int ParameterSetNr = 1;

            foreach (DataRow row in tableParameterSet.Rows)
            {
                OpenLearnFile(sender, e, Convert.ToInt64(row["LearnFileID"]));
                double SV = double.Parse(Convert.ToString(row["2"]));
                int PopulationSize = int.Parse(Convert.ToString(row["3"]));
                int NCount = 2;
                 int[] neuronsCount = { NCount, 0 };

                InitLearning(neuronsCount, SV, PopulationSize);
                StartTime = System.DateTime.Now;
                int m = ProcessLearn();
                DataTable tableCurves = curvesTableAdapter1.GetData();
                if (tableCurves.Rows.Count == 0)
                {
                    //TODO Automatisieren um ein Row hinzufuegen
                    MessageBox.Show("CurvesRow is 0, please add one row");
                }
                tableExperiments = experimentsTableAdapter1.GetData();
                experimentsTableAdapter1.Insert(tableExperiments.Rows.Count + 1,
                    Convert.ToInt64(row["ID"]), StartTime, System.DateTime.Now,
                     m, error, Convert.ToInt32(tableCurves.Rows[tableCurves.Rows.Count - 1]["ID"]));
                experimentsTableAdapter1.Update(DB.Experiments);
                //               tableExperiments.AcceptChanges();
                Invalidate();
                reader.Position = 0;
            }

            try
            {
                experimentsTableAdapter1.Update(DB.Experiments);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Update failed");
            }

            buttonStep.Enabled = true;
            buttonStopLearn.Enabled = false;

            reader.Close();
        }
        private void OpenLearnFile(object sender, EventArgs e, long LearnFileID)
        {
            //TODO can be better SQL
            DataTable tableLearnFile = getFilePathTableAdapter1.GetData();
            //           DataTable tableLearnFile = getFilePathTableAdapter1.GetDataByID(LearnFileID);
            //         String com = "SELECT  * \n FROM     GetFilePath \n WHERE  (ID =  " + LearnFileID.ToString() + ")";
            //          DataRow [] row = tableLearnFile.Select(com); 
            //
            LearnStream = tableLearnFile.Rows[0]["FilePath"].ToString();

            try
            {
                reader.Open(LearnStream);
                // read the video file
                framesCount = reader.Length;
            }
            catch (System.ApplicationException)
            {
                MessageBox.Show("File don´t exist");
            }
        }
        private void OpenLearnFile(object sender, EventArgs e)
        {
            openLearnFileDialog.ShowDialog();
            LearnStream = openLearnFileDialog.FileName;
            try
            {
                reader.Open(LearnStream);
                labelLearnFile.Text = "..." + LearnStream.Substring(LearnStream.Length - 52);
                framesCount = reader.Length;
                Deep = reader.Height;
            }
            catch (Exception)
            {
                MessageBox.Show("File don´t exist");
            }

            this.Invalidate();
        }
        /// <summary>
        /// load network file *.nw
        /// </summary>
        /// <param name="Path">file path without last slash \</param>
        /// <param name="FileName">file name with end suffix .nw</param>
        public void LoadNetworkFile(String Path, String FileName)
        {
            String FullFileName = Path + "\\" + FileName;

            if (File.Exists(FullFileName))
            {
                ActivationNetwork nw = (ActivationNetwork)Network.Load(FullFileName);
                network = nw;
                NetworkName = FileName.Remove(FileName.LastIndexOf('.'));
                labelName.Text = NetworkName;
            }
        }
        /// <summary>
        /// save network file *.nw
        /// </summary>
        /// <param name="Path">file path without last slash \</param>
        /// <param name="FileName">file name with end suffix .nw</param>
        public void SaveNetworkFile(String Path, String FileName)
        {
              network.Save(Path +"\\" + FileName);
              MessageBox.Show(" Network " + FileName + " saved.");
        }
        private void learnToolStripMenuItem_Click(object sender, EventArgs e)
        {
            buttonStopLearn.Enabled = true;
            OpenLearnFile(sender, e);

            Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
  
      //      Thread.CurrentThread.Name = "Um";
            Deep = reader.Height;
            int LearnSets = reader.Length;

            GetOutputsCount();

            input = new double[LearnSets][];
            output = new double[LearnSets][];
            outputReal = new double[OutputsCount];

            // anpassen Anzahl Output Neurons
     //       int[] neuronsCount = { NCount, 0 };
       //     neuronsCount[neuronsCount.Length - 1] = OutputsCount;

            bmRealitySnap = new Bitmap(2 * Deep, Deep, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmInput = new Bitmap(Deep, Deep, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmOutput = new Bitmap(Deep, Deep, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            for (int i = 0; i < LearnSets; i++)
            {
                input[i] = new double[Deep * Deep];
                output[i] = new double[OutputsCount];
            }

            // create neural network
            
            //network = new ActivationNetwork((IActivationFunction)new BipolarSigmoidFunction(SV),
            //    Deep * Deep,// inputs in the network
            //   neuronsCount);
            // create teacher

            int PopulationSize = 20;

            teacher = new EvolutionaryLearning(network, PopulationSize);

             // Set Learn Patterns
            while ((Step = reader.Position) < reader.Length)
            {
                bmRealitySnap = reader.GetNextFrame();
                SetInput(Step);
                // Abbilden Input Signal
                DrawInput(Step);
                SetOutput(Step);

                labelMinOutput.Text = output[Step][0].ToString();
                labelMaxOutput.Text = output[Step][1].ToString();
                labelLearnCycles.Text = Step.ToString();
                Invalidate();
            }
            // teach
            error = 100.0;

            int k = 0;
            double errorOld = 0;
            while (error > 1.0 & k < 200000 & buttonStopLearn.Enabled)
            {
                // run learning iteration
                error = teacher.RunEpoch(input, output);
                labelError.Text = error.ToString();
                labelLearnCycles.Text = k++.ToString();
                errorOld = error;
                // compute Output
                //          outputReal = network.Compute(input[0]);
                // show output
                DrawOutputReal();
                //            outputReal = network.Compute(input[1]);
                labelMinOutput.Text = outputReal[0].ToString();
                labelMaxOutput.Text = outputReal[1].ToString();
                // show output
                //             DrawOutputReal();
                Invalidate();
                Application.DoEvents();
            }

            reader.Close();

        }
        private void workToolStripMenuItem_Click(object sender, EventArgs e)
        {
             OpenLearnFile(sender, e);

          
            input = new double[2][];
            input [1] = new double [Deep*Deep];
            // TODO -1 ???

            Step = 0;
            if (Step < framesCount-1)
            {
                Step = reader.Position;
                labelLearnCycles.Text = Step.ToString();
                bmRealitySnap = reader.GetNextFrame();
                SetInput(1);
               // SetOutput(1);
                // Abbilden Input Signal
         //       DrawInput(1);
                // compute Output
                //TODO wenn war noch nicht gelernt Test funktioniert nicht
                outputReal = network.Compute(input[1]);
                // show output
     //           DrawOutputReal();
                labelMinOutput.Text = outputReal[0].ToString();
                labelMaxOutput.Text = outputReal[1].ToString();
                Refresh();
            }
      //      reader.Close();
            Invalidate();
        }
    }
}
